import {
  Tile,
  ParseOptions,
  ValidationResult,
  BoundingVolume,
  Tileset as TilesetInterface,
  Region
} from './types';
import { TilesetValidator } from './validator';

/**
 * Tileset 类
 * 提供解析、验证和操作 3D Tiles 数据集的功能
 */
export class TilesetClass {
  private data: TilesetInterface;
  private validator: TilesetValidator;

  /**
   * 创建一个 Tileset 实例
   * @param data Tileset 数据对象
   */
  constructor(data: TilesetInterface) {
    this.data = data;
    this.validator = new TilesetValidator();
  }

  /**
   * 解析 JSON 字符串为 Tileset 实例
   * @param json JSON 字符串
   * @param options 解析选项
   * @returns Tileset 实例
   */
  static parse(json: string, options: ParseOptions = {}): TilesetClass {
    try {
      const parsedData = JSON.parse(json);
      if (!parsedData || typeof parsedData !== 'object') {
        throw new Error('Invalid JSON format: Expected an object');
      }
      const tilesetData = parsedData as TilesetInterface;
      const tileset = new TilesetClass(tilesetData);

      if (options.validate) {
        const result = tileset.validate();
        if (!result.valid) {
          throw new Error(`Validation failed: ${result.errors.join('; ')}`);
        }
      }

      return tileset;
    } catch (error) {
      throw new Error(`Failed to parse tileset: ${(error as Error).message}`);
    }
  }

  /**
   * 从对象创建 Tileset 实例
   * @param data Tileset 数据对象
   * @param options 解析选项
   * @returns Tileset 实例
   */
  static fromObject(data: TilesetInterface, options: ParseOptions = {}): TilesetClass {
    if (!data || typeof data !== 'object') {
      throw new Error('Invalid input: Expected an object conforming to Tileset interface');
    }

    const tileset = new TilesetClass(data);

    if (options.validate) {
      const result = tileset.validate();
      if (!result.valid) {
        throw new Error(`Validation failed: ${result.errors.join('; ')}`);
      }
    }

    return tileset;
  }

  /**
   * 验证当前 tileset 的结构
   * @returns 验证结果
   */
  validate(): ValidationResult {
    return this.validator.validate(this.data);
  }

  /**
   * 将 tileset 转换为 JSON 字符串
   * @param space 缩进空格数
   * @returns JSON 字符串
   */
  toJSON(space?: number): string {
    return JSON.stringify(this.data, null, space);
  }

  /**
   * 获取原始数据对象
   * @returns Tileset 数据对象
   */
  toObject(): TilesetInterface {
    // 返回深拷贝，避免外部修改内部数据
    return JSON.parse(JSON.stringify(this.data));
  }

  /**
   * 获取根瓦片
   * @returns 根瓦片
   */
  getRootTile(): Tile {
    return this.data.root;
  }

  /**
   * 获取所有瓦片（递归）
   * @returns 所有瓦片的数组
   */
  getAllTiles(): Tile[] {
    const tiles: Tile[] = [];
    this.traverse((tile) => tiles.push(tile));
    return tiles;
  }

  /**
   * 遍历所有瓦片
   * @param callback 处理每个瓦片的回调函数
   * @param tile 起始瓦片，默认为根瓦片
   */
  traverse(callback: (tile: Tile, depth: number) => void, tile: Tile = this.data.root, depth = 0): void {
    callback(tile, depth);

    if (Array.isArray(tile.children)) {
      tile.children.forEach((child) => {
        this.traverse(callback, child, depth + 1);
      });
    }
  }

  /**
   * 查找符合条件的瓦片
   * @param predicate 判断条件
   * @returns 第一个符合条件的瓦片，或 undefined
   */
  findTile(predicate: (tile: Tile) => boolean): Tile | undefined {
    let result: Tile | undefined;

    this.traverse((tile) => {
      if (!result && predicate(tile)) {
        result = tile;
      }
    });

    return result;
  }

  /**
   * 查找所有符合条件的瓦片
   * @param predicate 判断条件
   * @returns 所有符合条件的瓦片数组
   */
  findAllTiles(predicate: (tile: Tile) => boolean): Tile[] {
    const results: Tile[] = [];

    this.traverse((tile) => {
      if (predicate(tile)) {
        results.push(tile);
      }
    });

    return results;
  }

  /**
   * 获取瓦片总数
   * @returns 瓦片总数
   */
  getTileCount(): number {
    let count = 0;
    this.traverse(() => count++);
    return count;
  }

  /**
   * 获取数据集版本
   * @returns 版本字符串
   */
  getVersion(): string {
    return this.data.asset.version;
  }

  /**
   * 获取自定义属性
   * @returns 属性对象
   */
  getProperties(): Record<string, any> | undefined {
    return this.data.properties;
  }

  /**
   * 设置自定义属性
   * @param properties 新的属性对象
   */
  setProperties(properties: Record<string, any>): void {
    this.data.properties = properties;
  }

  /**
   * 获取使用的扩展
   * @returns 扩展名称数组
   */
  getExtensionsUsed(): string[] | undefined {
    return this.data.extensionsUsed;
  }

  /**
   * 添加扩展到扩展列表
   * @param extension 扩展名称
   */
  addExtension(extension: string): void {
    if (!this.data.extensionsUsed) {
      this.data.extensionsUsed = [];
    }

    if (!this.data.extensionsUsed.includes(extension)) {
      this.data.extensionsUsed.push(extension);
    }
  }

  /**
   * 计算整个数据集的边界体积
   * @returns 边界体积
   */
  computeOverallBoundingVolume(): BoundingVolume {
    const allTiles = this.getAllTiles();
    const boundingVolumes = allTiles.map(tile => tile.boundingVolume);

    const mergedRegion = boundingVolumes
      .filter(bv => bv.region)
      .reduce((merged, current) => {
        const region = current.region!;
        return [
          Math.min(merged[0], region[0]), // 西经
          Math.min(merged[1], region[1]), // 南纬
          Math.max(merged[2], region[2]), // 东经
          Math.max(merged[3], region[3]), // 北纬
          Math.min(merged[4], region[4]), // 最小高度
          Math.max(merged[5], region[5])  // 最大高度
        ];
      }, [Infinity, Infinity, -Infinity, -Infinity, Infinity, -Infinity]);

    return { region: mergedRegion as Region };
  }
}